﻿@page "/stickers"
@using System.Net
@using Microsoft.AspNetCore.Authorization
@using System.Text.Json
@using System.Text
@using global::Stickers.Web.Components
@using global::Stickers.Web.ViewModels

@inject IHttpClientFactory HttpClientFactory
@attribute [Authorize]

<PageTitle>我的贴纸墙</PageTitle>

<div class="container-fluid">
    <div class="row">
        <div class="d-flex align-items-center">
            <div class="input-group">
                <span class="input-group-text" id="basic-addon1">
                    <Icon Name="IconName.Search"></Icon>
                </span>
                <input type="text" class="form-control" placeholder="输入搜索文本" aria-label="Search" aria-describedby="basic-addon1">
            </div>
            <Button Color="ButtonColor.Primary"
                    Class="ms-2"
                    Style="min-width: 90px;"
                    TooltipPlacement="TooltipPlacement.Bottom"
                    TooltipTitle="新建贴纸"
                    @onclick="async () => await EditStickerAsync()">
                <Icon Name="IconName.FileEarmarkPlus"></Icon> 新建
            </Button>
        </div>
    </div>

    <div class="row">
        @if (CurrentPage is null)
        {
        <p><em>正在加载...</em></p>
        }
        else
        {
        @if (CurrentPage.TotalCount == 0)
        {
        <h3 class="text-center no-sticker">您还没有创建任何贴纸，请点击【新建】按钮创建。</h3>
        }
        else
        {
        @foreach (var sticker in CurrentPage.Stickers)
        {
        <div class="col-sm-3">
            <Card Color="CardColor.Warning" Class="mb-4" Style="margin-top: 10px;">
                <CardHeader>
                    <strong>
                        <Icon Name="IconName.Bookmark"></Icon>
                        <a href="javascript:void(0)" @onclick="async () => await EditStickerAsync(new Sticker { Id = sticker.Id, Title = sticker.Title, Content = sticker.Content })">
                            @sticker.Title
                        </a>
                    </strong>
                </CardHeader>
                <CardBody>
                    <CardText><small>@sticker.Content</small></CardText>
                </CardBody>
                <CardFooter>
                    <div class="text-center d-flex justify-content-between">
                        <a href="javascript:void(0)" class="trash-bin" @onclick="async() => await ConfirmDeleteAsync(sticker.Id)"><Icon Name="IconName.Trash"></Icon> </a>
                        <span class="dt"><Icon Name="IconName.Calendar2Date"></Icon> @($"{sticker.DateCreated.ToLocalTime():M/d/yy HH:mm:ss}")</span>
                    </div>
                </CardFooter>
            </Card>
        </div>
        }
        }
        }
    </div>
    @if (CurrentPage is not null && CurrentPage.TotalPages > 1)
    {
    <div class="row">
        <Pagination ActivePageNumber="@CurrentPage.PageIndex"
                    Alignment="Alignment.Center"
                    TotalPages="@CurrentPage.TotalPages"
                    DisplayPages="8"
                    Size="PaginationSize.Small"
                    FirstLinkIcon="IconName.ChevronDoubleLeft"
                    PreviousLinkIcon="IconName.ChevronLeft"
                    NextLinkIcon="IconName.ChevronRight"
                    LastLinkIcon="IconName.ChevronDoubleRight"
                    PageChanged="OnPageChanged" />
    </div>
    }
</div>

<Modal @ref="_editorModal" />
<ConfirmDialog @ref="_deleteConfirmDlg"></ConfirmDialog>

@code {

    private const int DefaultPageSize = 12;

    private Modal? _editorModal;

    private StickersPage? CurrentPage { get; set; }

    [Inject]
    private ToastService? ToastService { get; set; }

    private ConfirmDialog? _deleteConfirmDlg;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            CurrentPage = await ReadStickersAsync();
        }
        catch (HttpRequestException ex) when (ex.StatusCode is HttpStatusCode.Forbidden)
        {
            return;
        }
        
        await base.OnInitializedAsync();
    }

    private async Task<StickersPage?> ReadStickersAsync(int pageNumber = 1, int pageSize = DefaultPageSize)
    {
        using var httpClient = HttpClientFactory.CreateClient("stickersHttpClient");
        var httpResponseMessage = await httpClient.GetAsync($"api/stickers?page={pageNumber}&size={pageSize}");
        httpResponseMessage.EnsureSuccessStatusCode();
        var responseJson = await httpResponseMessage.Content.ReadAsStringAsync();
        return JsonSerializer.Deserialize<StickersPage>(responseJson);
    }

    private async Task<int> CreateStickerAsync(string title, string content)
    {
        var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "api/stickers");
        using var memoryStream = new MemoryStream();
        await JsonSerializer.SerializeAsync(memoryStream, new { title, content });
        var payloadJson = Encoding.UTF8.GetString(memoryStream.ToArray());
        httpRequestMessage.Content = new StringContent(payloadJson, Encoding.UTF8, "application/json");
        using var httpClient = HttpClientFactory.CreateClient("stickersHttpClient");
        var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
        httpResponseMessage.EnsureSuccessStatusCode();
        var responseJson = await httpResponseMessage.Content.ReadAsStringAsync();
        var stickerCreated = JsonSerializer.Deserialize<Sticker>(responseJson);
        if (stickerCreated != null) return stickerCreated.Id;
        throw new InvalidOperationException("无法创建贴纸。");
    }

    private async Task UpdateStickerAsync(int id, string title, string content)
    {
        var httpRequestMessage = new HttpRequestMessage(HttpMethod.Patch, $"api/stickers/{id}");
        using var memoryStream = new MemoryStream();
        await JsonSerializer.SerializeAsync(memoryStream, new[]
        {
            new
            {
                op = "replace",
                path = "/title",
                value = title
            },
            new
            {
                op = "replace",
                path = "/content",
                value = content
            }
        });
        var payloadJson = Encoding.UTF8.GetString(memoryStream.ToArray());
        httpRequestMessage.Content = new StringContent(payloadJson, Encoding.UTF8, "application/json-patch+json");
        using var httpClient = HttpClientFactory.CreateClient("stickersHttpClient");
        var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
        httpResponseMessage.EnsureSuccessStatusCode();
    }

    private async Task DeleteStickerAsync(int id)
    {
        var httpRequestMessage = new HttpRequestMessage(HttpMethod.Delete, $"api/stickers/{id}");
        using var httpClient = HttpClientFactory.CreateClient("stickersHttpClient");
        var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage);
        httpResponseMessage.EnsureSuccessStatusCode();
    }

    private async Task CloseEditorAsync(MouseEventArgs e) => await DoCloseEditorAsync();

    private async Task EditStickerAsync(Sticker? sticker = null)
    {
        if (_editorModal is not null)
        {
            var editMode = sticker is null ? EditMode.Create : EditMode.Edit;
            var model = new StickerEditModel
                {
                    EditMode = editMode,
                    Title = sticker?.Title,
                    Content = sticker?.Content,
                    Id = sticker?.Id ?? 0
                };

            var parameters = new Dictionary<string, object>
            {
                { "Model", model },
                { "OnCloseClickCallback", EventCallback.Factory.Create<MouseEventArgs>(this, CloseEditorAsync) },
                { "OnSaveClickCallback", EventCallback.Factory.Create<StickerEditModel>(this, SaveAsync) }
            };

            await _editorModal.ShowAsync<EditStickerComponent>(editMode == EditMode.Create ? "新建贴纸" : "编辑贴纸", parameters: parameters);
        }
    }

    private async Task ConfirmDeleteAsync(int id)
    {
        if (_deleteConfirmDlg is not null)
        {
            var confirmResult = await _deleteConfirmDlg.ShowAsync("是否确认删除当前贴纸？", "贴纸删除后将无法恢复，是否继续？", new ConfirmDialogOptions
                {
                    YesButtonColor = ButtonColor.Danger,
                    YesButtonText = "删除",
                    AutoFocusYesButton = false,
                    NoButtonColor = ButtonColor.Secondary,
                    NoButtonText = "取消"
                });

            if (confirmResult)
            {
                await DeleteStickerAsync(id);
                CurrentPage = await ReadStickersAsync(CurrentPage?.PageIndex ?? 1, CurrentPage?.PageSize ?? DefaultPageSize);
            }
        }
    }

    private async Task SaveAsync(StickerEditModel model)
    {
        if (string.IsNullOrEmpty(model.Title) || string.IsNullOrEmpty(model.Content))
        {
            ToastService?.Notify(new(ToastType.Danger, IconName.ExclamationTriangleFill, "错误", "请输入标题和内容。"));
            return;
        }

        switch (model.EditMode)
        {
            case EditMode.Create:
                try
                {
                    _ = await CreateStickerAsync(model.Title, model.Content);
                    ToastService?.Notify(new(ToastType.Success, IconName.ExclamationTriangleFill, "成功", "已成功创建贴纸。"));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    ToastService?.Notify(new(ToastType.Danger, IconName.ExclamationTriangleFill, "错误", e.Message));
                    return;
                }
                break;
            case EditMode.Edit:
                try
                {
                    await UpdateStickerAsync(model.Id, model.Title, model.Content);
                    ToastService?.Notify(new(ToastType.Success, IconName.ExclamationTriangleFill, "成功", "已成功更新贴纸。"));
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    ToastService?.Notify(new(ToastType.Danger, IconName.ExclamationTriangleFill, "错误", e.Message));
                    return;
                }
                break;
        }

        await DoCloseEditorAsync();

        CurrentPage = await ReadStickersAsync(CurrentPage?.PageIndex ?? 1, CurrentPage?.PageSize ?? DefaultPageSize);
    }

    private async Task OnPageChanged(int newPageNumber) => CurrentPage = await ReadStickersAsync(newPageNumber);

    private async Task DoCloseEditorAsync()
    {
        if (_editorModal is not null) await _editorModal.HideAsync();
    }
}
